package com.taoyun.service.impl;

import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.*;
import com.taoyun.config.ObsConfig;
import com.taoyun.mapper.*;
import com.taoyun.pojo.entity.FileBelongGroup;
import com.taoyun.pojo.entity.U_send_F;
import com.taoyun.pojo.entity.User;
import com.taoyun.service.IObsService;
import com.taoyun.utils.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.Max;
import javax.validation.constraints.Null;
import java.io.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 *  华为云 obs相关操作
 *
 * @author FengJingdong
 */
@Service
public class ObsServiceImpl implements IObsService {
    @Autowired
    private ObsClient obsClient;

    @Autowired
    private ObsConfig obsConfig;

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private USendFMapper uSendFMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private GroupMapper groupMapper;

    @Autowired
    private FileBelongGroupMapper fileBelongGroupMapper;

    //群组文件夹最大存储空间
    private long GroupMaxSize = 5368709120L;

    //用户文件夹最大存储空间
    private long UserMaxSize = 2147483648L;

    public List<ObsObject> getAllObjects() throws IOException {
        ObjectListing objectListing = obsClient.listObjects(obsConfig.getBucketName());
        List<ObsObject> list = objectListing.getObjects();
        obsClient.close();
        return list;
    }

    @Override
    public String deleteObjects(List<String> objectNames) {
        DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(obsConfig.getBucketName());
        for (String objectName : objectNames) {
            deleteRequest.addKeyAndVersion(objectName);
        }
        DeleteObjectsResult deleteObjectsResult = obsClient.deleteObjects(deleteRequest);
        StringBuilder error = new StringBuilder("以下object删除失败：");
        for (DeleteObjectsResult.ErrorResult errorResult : deleteObjectsResult.getErrorResults()) {
            error.append(errorResult.getObjectKey());
            error.append(" ");
        }
        // 有出错信息则error以 ！ 结束
        if (deleteObjectsResult.getErrorResults().size() > 0) {
            error.append("!");
        }
        return error.toString();
    }

    @Override
    public boolean deleteObject(String objectName) {
        obsClient.deleteObject(obsConfig.getBucketName(), objectName);
        return true;
    }

    @Override
    public String copyObject(String sobjectName, String dobjectName) {
        CopyObjectResult result = obsClient.copyObject(obsConfig.getBucketName(), sobjectName, obsConfig.getBucketName(), dobjectName);
        System.out.println("\t" + result.getStatusCode());
        return result.getEtag();
    }

    @Override
    public String moveObject(String sobjectName, String dobjectName) {
        obsClient.copyObject(obsConfig.getBucketName(), sobjectName, obsConfig.getBucketName(), dobjectName);
        DeleteObjectResult result = obsClient.deleteObject(obsConfig.getBucketName(), sobjectName);
        return result.toString();
    }

    @Override
    public void renameObjects(String objectName, String newObjectName) {
        // 重命名对象
        RenameObjectRequest request = new RenameObjectRequest();
        request.setBucketName(obsConfig.getBucketName());
        request.setObjectKey(objectName);
        request.setNewObjectKey(newObjectName);
        RenameObjectResult result = obsClient.renameObject(request);
    }

    @Override
    public String getObjectLink(long expireSeconds, String objectName) {
        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expireSeconds);
        request.setBucketName(obsConfig.getBucketName());
        request.setObjectKey(objectName);

        TemporarySignatureResponse response = obsClient.createTemporarySignature(request);

        return response.getSignedUrl();

    }

    @Override
    public void createFileSystem(String bucketName) throws IOException {
        try {
            // 创建并行文件系统成功
            CreateBucketRequest request = new CreateBucketRequest();
            request.setBucketName(bucketName);
            request.setBucketType(BucketTypeEnum.PFS);
            // 指定所属区域
            // 这里可能要改  如果服务器变位置的话
            request.setLocation("cn-north-4");
            HeaderResponse response = obsClient.createBucket(request);
            System.out.println(response.getRequestId());
        } catch (ObsException e) {
            // 创建并行文件系统失败
            System.out.println("HTTP Code: " + e.getResponseCode());
            System.out.println("Error Code:" + e.getErrorCode());
            System.out.println("Error Message: " + e.getErrorMessage());

            System.out.println("Request ID:" + e.getErrorRequestId());
            System.out.println("Host ID:" + e.getErrorHostId());
        }
    }



    @Override
    //断点续传
    public Response downloadfile(String bucketName, String objectNames, String fileLocatePrefix) throws IOException {


        //解析前端传来的文件名信息
        String[] ob = objectNames.split(",");

        for (int i = 0; i < ob.length; i++) {

            //根据最后一个字符是否为"/"判断下载的是文件还是文件夹
            if (!ob[i].substring(ob[i].length() - 1).equals("/")) {
                DownloadFileRequest requestDownload = new DownloadFileRequest(bucketName, ob[i]);

                //因为是下载的是文件，所以只要/之后的文件名
                String LocatePrefix = ob[i].split("/")[ob[i].split("/").length - 1];

                //判断本地是否已存在该文件
                File file = new File(fileLocatePrefix + LocatePrefix);
                if (file.exists()) {
                    return Response.error(400,"文件" + LocatePrefix + "已存在本地目录，下载失败");

                } else {
                    //设置下载文件的本地位置
                    requestDownload.setDownloadFile(fileLocatePrefix + LocatePrefix);
                    // 设置分段下载时的最大并发数
                    requestDownload.setTaskNum(5);
                    // 设置分段大小为10MB
                    requestDownload.setPartSize(10 * 1024 * 1024);
                    // 开启断点续传模式
                    requestDownload.setEnableCheckpoint(true);

                    try {
                        // 进行断点续传下载
                        DownloadFileResult resultDownload = obsClient.downloadFile(requestDownload);
                    } catch (ObsException e) {
                        e.printStackTrace();
                    }
                    HttpServletResponse resp = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                    if (file.exists()) {
                        resp.setContentType("application/x-msdownload");
                        resp.setHeader("Content-Disposition", "attachment;filename=" + new String(LocatePrefix.getBytes(), "ISO-8859-1"));
                        InputStream inputStream = new FileInputStream(file);
                        ServletOutputStream ouputStream = resp.getOutputStream();
                        byte b[] = new byte[1024];
                        int n;
                        while ((n = inputStream.read(b)) != -1) {
                            ouputStream.write(b, 0, n);
                        }
                        ouputStream.close();
                        inputStream.close();
                    }
                }
            }
            //下载的是文件夹
            else {
                //列出桶内的文件
                ListObjectsRequest request = new ListObjectsRequest(bucketName);

                request.setPrefix(ob[i]);

                request.setMaxKeys(500);


                /*
                 * 取出路径多余部分
                 * 下载456/123/，就是下载123文件夹
                 * 456/就是多余部分
                 * */
                String N = "";
                if (ob[i].split("/").length > 1) {
                    N = ob[i].substring(0, ob[i].lastIndexOf("/", ob[i].lastIndexOf("/") - 1)) + "/";
                }
                System.out.println("多余部分：" + N);


                ObjectListing result;

                do {
                    result = obsClient.listObjects(request);
                    for (ObsObject obsObject : result.getObjects()) {
                        System.out.println("\t" + obsObject.getObjectKey());
                        //选出文件夹
                        if (obsObject.getObjectKey().substring(obsObject.getObjectKey().length() - 1).equals("/")) {

                            //从多余部分开始后面的路径就是下载路径
                            String path = obsObject.getObjectKey().substring(N.length());
                            //System.out.println("path:"+path);
                            //没有多余路径存在，就去除后面的“/”
                            String LocatePrefix = path.substring(0, path.length() - 1);

                            File file = new File(fileLocatePrefix + LocatePrefix);
                            if (!file.exists() && !file.isDirectory()) {
                                file.mkdirs();
                            } else {
                                return Response.buildCustom(400,"文件夹" + fileLocatePrefix + LocatePrefix + "已存在，下载失败");
                            }
                        } else {
                            //文件夹下没有下一层文件夹直接下载文件
                            DownloadFileRequest requestDownload = new DownloadFileRequest(bucketName, obsObject.getObjectKey());
                            String LocatePrefix = obsObject.getObjectKey().substring(N.length());

                            //设置下载文件的本地位置
                            requestDownload.setDownloadFile(fileLocatePrefix + LocatePrefix);
                            // 设置分段下载时的最大并发数
                            requestDownload.setTaskNum(5);
                            // 设置分段大小为10MB
                            requestDownload.setPartSize(10 * 1024 * 1024);
                            // 开启断点续传模式
                            requestDownload.setEnableCheckpoint(true);

                            try {
                                // 进行断点续传下载
                                DownloadFileResult resultDownload = obsClient.downloadFile(requestDownload);
                            } catch (ObsException e) {
                                //发生异常再次调用接口重新下载
                                DownloadFileResult resultDownload = obsClient.downloadFile(requestDownload);
                                //打印信息错误信息
                                return Response.buildCustom(400, "下载文件发生异常，开始重新下载");
                            }
                        }
                    }
                    System.out.println("------------------------------");

                    request.setMarker(result.getNextMarker());   //返回 NextMarker作为下次读取的起点

                } while (result.isTruncated());  //截断
            }
        }
        return Response.success("下载成功");
    }




    @Override
    //获取下载进度
    public void downloadprogress(String bucketName, String objectName) throws IOException {
        GetObjectRequest request = new GetObjectRequest(bucketName, objectName);
        request.setProgressListener(new ProgressListener() {


            public void progressChanged(ProgressStatus status) {
                // 获取下载平均速率
                System.out.println("AverageSpeed:" + status.getAverageSpeed());
                // 获取下载进度百分比
                System.out.println("TransferPercentage:" + status.getTransferPercentage());
            }
        });
        // 每上传1MB数据反馈下载进度
        request.setProgressInterval(1 * 1L);
        ObsObject obsObject = obsClient.getObject(request);

        // 读取对象内容
        System.out.println("Object content:");
        InputStream input = obsObject.getObjectContent();
        byte[] b = new byte[1024];
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        int len;
        while ((len = input.read(b)) != -1) {
            bos.write(b, 0, len);
        }

        System.out.println(new String(bos.toByteArray()));
        bos.close();
        input.close();

    }

    //获取上传进度 --未完善
    @Override
    public void uploadprogress(String bucketName, String objectName) throws IOException {
        //ObsClient obsClient = new ObsClient(ak, sk, endPoint);
        PutObjectRequest request = new PutObjectRequest("bucketname", "objectname");
        //request.setFile(new File("localfile"));
        request.setProgressListener(new ProgressListener() {
            @Override
            public void progressChanged(ProgressStatus status) {
                // 获取上传平均速率
                System.out.println("AverageSpeed:" + status.getAverageSpeed());
                // 获取上传进度百分比
                System.out.println("TransferPercentage:" + status.getTransferPercentage());
            }
        });
        // 每上传1MB数据反馈上传进度
        request.setProgressInterval(1024 * 1024L);
        obsClient.putObject(request);
    }

    @Override
    public void showobjectName(String bucketName, String objectName) throws IOException {
        ObjectMetadata metadata = obsClient.getObjectMetadata(bucketName, objectName);
        System.out.println("\t" + metadata.getContentType());   //显示消息类型
        System.out.println("\t" + metadata.getContentLength());  //显示消息长度
//        System.out.println("\t" + metadata.getUserMetadata("property"));  //显示用户元数据
        System.out.println("\t" + metadata.getValue(objectName));  //显示用户元数据


    }

    @Override
    public String shareobject(String url, String dobj) {
        int location = 0;
        int lastIndex = 0;
        int firstIndex = url.indexOf("/");//*第一个出现的索引位置
        while (firstIndex != -1) {
            firstIndex = url.indexOf("/", firstIndex + 1);//*从这个索引往后开始第一个出现的位置
            location++;
            if (location == 2) {
                lastIndex = url.indexOf("?");
                url = url.substring(firstIndex + 1, lastIndex);
            }
        }
        System.out.println(url);
        CopyObjectResult result = obsClient.copyObject(obsConfig.getBucketName(), url, obsConfig.getBucketName(), dobj);
        return result.getEtag();
    }

    /**
     * 添加员工、创建群组的同时，在obs上为员工、群组创建根目录文件夹，同时在数据库中插入对应的记录
     *添加员工时创建文件夹，插入file、u_send_f、objectSize表；
     * 创建群组时创建文件夹，插入file、F_belong_G、objectSize表
     * @param pathName (应为 (groud)personal/工号(工号后没有/)..)
     * @param author 创建该文件夹的作者
     * @return 0(固定 ， 没有的 ）
     * @throws IOException
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false)
    public Boolean putObject(String pathName, String author, String type, Integer userId) throws IOException {
        Boolean result = false;
        String bucketName = obsConfig.getBucketName();
        if(pathName.indexOf("//") != -1 || pathName.equals("/")){
            return false;
        }
        try {
            Integer finalResult = 0;
            long MaxSize = 0;
            Integer fileId = 0;
            //获取创建文件夹的类别
            if(type.equals("group")){
                //从传入的objectName(group/xxxx)中获取groupId
                Integer groupId = Integer.valueOf(pathName.substring(pathName.indexOf("/") + 1));
                System.out.println(groupId);
                //添加file表记录
                fileMapper.insertFile("groups", "groups/", groupId.toString(), pathName, author, "", new Date(), "folder", null);
                fileId = fileMapper.selectFileIdByPathName(pathName);
                System.out.println(fileId);
                //设置最大存储空间
                MaxSize = GroupMaxSize;
                //根据groupId以及userId获取G_Id
                Integer G_Id = groupMapper.SelectGroupByIdAndUId(groupId, userId).getGId();
                System.out.println(G_Id);
                FileBelongGroup fileBelongGroup = new FileBelongGroup(G_Id, fileId, groupId);
                //添加F_Belong_G表的相关记录
                finalResult = fileBelongGroupMapper.addFBelongG(fileBelongGroup);
            }else if(type.equals("user")){
                //从传入的objectName(group/xxxx)中获取groupId
                String userNumber = pathName.substring(pathName.indexOf("/") + 1);
                //添加file表记录
                fileMapper.insertFile("user", "user/", userNumber, pathName, author, "", new Date(), "folder", null);
                fileId = fileMapper.selectFileIdByPathName(pathName);
                //设置最大存储空间
                MaxSize = UserMaxSize;
                //添加U_send_F表的相关记录
                U_send_F u_send_f = new U_send_F(userId, fileId, new Date());
                uSendFMapper.insert(u_send_f);
                finalResult = 1;
            }else {
                return false;
            }
            //添加ObjectSize表记录
            fileMapper.insert_ObjectSize(pathName + "/", MaxSize);
            if(finalResult == 1){

                //在obs上创建文件夹
                PutObjectRequest request = new PutObjectRequest();
                request.setBucketName(bucketName);
                request.setObjectKey(pathName + "/");
                request.setInput(new ByteArrayInputStream(new byte[0]));
                obsClient.putObject(request);
                result = true;
            }else {
                result = false;
            }
        } catch (ObsException e) {
            System.out.println("Response Code: " + e.getResponseCode());
            System.out.println("Error Message: " + e.getErrorMessage());
            System.out.println("Error Code:       " + e.getErrorCode());
            System.out.println("Request ID:      " + e.getErrorRequestId());
            System.out.println("Host ID:           " + e.getErrorHostId());
        }
        return result;
    }

    /** 删除员工/群组的同时，删除obs文件夹或文件
     *
     * @param objectName (lib/test/或者lib/hello.c...)
     * @return 0失败，1成功
     */
    @Override
    public int delete_Obsobject(String objectName) {
        String bucketName = obsConfig.getBucketName();
        DeleteObjectsRequest request = new DeleteObjectsRequest();
        request.setBucketName(bucketName);
        request.setQuiet(false);
        List<String> keys = new ArrayList<String>();
        ListObjectsRequest re = new ListObjectsRequest(bucketName);
        ObjectListing result;
        re.setPrefix(objectName);
        do {
            result = obsClient.listObjects(re);
            for (ObsObject obsObject : result.getObjects()) {
                keys.add(obsObject.getObjectKey());
            }
            re.setMarker(result.getNextMarker());
        } while (result.isTruncated());

        if(keys.size() == 0){
            return 0;
        }
        KeyAndVersion[] kvs = new KeyAndVersion[keys.size()];
        int index = 0;
        for (String key : keys) {
            kvs[index++] = new KeyAndVersion(key);
        }
        request.setKeyAndVersions(kvs);
        DeleteObjectsResult deleteObjectsResult = obsClient.deleteObjects(request);

        return 1;
    }

    @Override
    public List<String> getDownloadLink(List<String> objectNames) {
        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, 3600);
        request.setBucketName(obsConfig.getBucketName());
        List<String> Links=new ArrayList<String>();

        for(int i=0;i<objectNames.size();i++){
            request.setObjectKey(objectNames.get(i));
            Links.add(obsClient.createTemporarySignature(request).getSignedUrl());
        }

        return Links;

    }
}